﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace LumenLiquid_Tool
{
    public class NavigationGridAnimation : DependencyObject
    {
        public enum EDirection
        {
            Left,
            Right,
            Up,
            Down,
        }

        public static readonly DependencyProperty DirectionProperty =
            DependencyProperty.Register("Direction", typeof(EDirection), typeof(NavigationGridAnimation), new PropertyMetadata(EDirection.Left));

        public EDirection Direction
        {
            get { return (EDirection)GetValue(DirectionProperty); }
            set { SetValue(DirectionProperty, value); }
        }

        public static readonly DependencyProperty EasingFunctionProperty =
            DependencyProperty.Register("EasingFunction", typeof(IEasingFunction), typeof(NavigationGridAnimation), new PropertyMetadata(default(IEasingFunction)));

        public IEasingFunction EasingFunction
        {
            get { return (IEasingFunction)GetValue(EasingFunctionProperty); }
            set { SetValue(EasingFunctionProperty, value); }
        }

        public static readonly DependencyProperty DurationProperty =
            DependencyProperty.Register("Duration", typeof(Duration), typeof(NavigationGridAnimation), new PropertyMetadata(new Duration(new TimeSpan(0, 0, 0, 0, 200))));

        public Duration Duration
        {
            get { return (Duration)GetValue(DurationProperty); }
            set { SetValue(DurationProperty, value); }
        }
    }

    public class NavigationGrid : Panel
    {
        #region Properties
        #region NormalProperties

        protected static readonly DependencyPropertyKey CurrentPanelKey =
            DependencyProperty.RegisterReadOnly("CurrentPanel", typeof(string), typeof(NavigationGrid), new PropertyMetadata(default(string), CurrentPanelChanged));

        public static readonly DependencyProperty CurrentPanelProperty = CurrentPanelKey.DependencyProperty;

        public string CurrentPanel
        {
            get { return (string)GetValue(CurrentPanelProperty); }
            protected set { SetValue(CurrentPanelKey, value); }
        }

        public Panel CurrentObject()
        {
            return FindWithMetadata(PanelNameProperty, CurrentPanel) as Panel;
        }

        private static void CurrentPanelChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var parent = (NavigationGrid)obj;

            parent.OldPanel = (string)args.OldValue;

            UIElement OldPanel = parent.FindWithMetadata(PanelNameProperty, args.OldValue);
            UIElement NewPanel = parent.FindWithMetadata(PanelNameProperty, args.NewValue);

            RectAnimation fadeOut = new RectAnimation();
            RectAnimation fadeIn = new RectAnimation();

            fadeOut.From = new Rect(0, 0, 1, 1);
            fadeIn.To = new Rect(0, 0, 1, 1);
            fadeOut.Duration = parent.NextTransition.Duration;
            fadeIn.Duration = parent.NextTransition.Duration;
            fadeIn.EasingFunction = parent.NextTransition.EasingFunction;
            fadeOut.EasingFunction = parent.NextTransition.EasingFunction;

            switch (parent.NextTransition.Direction)
            {
                case NavigationGridAnimation.EDirection.Left:
                    fadeOut.To = new Rect(-1, 0, 1, 1);
                    fadeIn.From = new Rect(1, 0, 1, 1);
                    break;
                case NavigationGridAnimation.EDirection.Right:
                    fadeOut.To = new Rect(1, 0, 1, 1);
                    fadeIn.From = new Rect(-1, 0, 1, 1);
                    break;
                case NavigationGridAnimation.EDirection.Down:
                    fadeOut.To = new Rect(0, 1, 1, 1);
                    fadeIn.From = new Rect(0, -1, 1, 1);
                    break;
                case NavigationGridAnimation.EDirection.Up:
                    fadeOut.To = new Rect(0, -1, 1, 1);
                    fadeIn.From = new Rect(0, 1, 1, 1);
                    break;
            }

            if (OldPanel != null)
            {
                OldPanel.BeginAnimation(PositionProperty, fadeOut);
            }

            if (NewPanel != null)
            {
                NewPanel.BeginAnimation(PositionProperty, fadeIn);
            }
        }

        protected static readonly DependencyPropertyKey OldPanelKey =
            DependencyProperty.RegisterReadOnly("OldPanel", typeof (string), typeof (NavigationGrid), new PropertyMetadata(default(string)));

        public static readonly DependencyProperty OldPanelProperty = OldPanelKey.DependencyProperty;

        public string OldPanel
        {
            get { return (string) GetValue(OldPanelProperty); }
            protected set { SetValue(OldPanelKey, value); }
        }

        public static readonly DependencyProperty StartupPanelProperty =
           DependencyProperty.Register("StartupPanel", typeof(string), typeof(NavigationGrid), new PropertyMetadata("", StartupPanelChanged));

        public string StartupPanel
        {
            get { return (string)GetValue(StartupPanelProperty); }
            set { SetValue(StartupPanelProperty, value); }
        }

        private static void StartupPanelChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var parent = obj as NavigationGrid;

            if (!parent.noStartupPanel)
            {
                parent.CurrentPanel = (string)args.NewValue;
            }
        }

        protected static readonly DependencyProperty NextTransitionProperty =
            DependencyProperty.Register("NextTransition", typeof(NavigationGridAnimation), typeof(NavigationGrid), new PropertyMetadata(default(NavigationGridAnimation)));

        protected NavigationGridAnimation NextTransition
        {
            get { return (NavigationGridAnimation)GetValue(NextTransitionProperty); }
            set { SetValue(NextTransitionProperty, value); }
        }
        #endregion

        #region Attached Properties
        public static readonly DependencyProperty PanelNameProperty =
            DependencyProperty.RegisterAttached("PanelName", typeof(string), typeof(NavigationGrid), new PropertyMetadata(default(string)));

        [AttachedPropertyBrowsableForChildren]
        public static void SetPanelName(Panel element, string value)
        {
            element.SetValue(PanelNameProperty, value);
        }

        [AttachedPropertyBrowsableForChildren]
        public static string GetPanelName(Panel element)
        {
            return (string)element.GetValue(PanelNameProperty);
        }

        public static readonly DependencyProperty TargetPanelProperty =
            DependencyProperty.RegisterAttached("TargetPanel", typeof(string), typeof(NavigationGrid), new PropertyMetadata(default(string)));

        [AttachedPropertyBrowsableForChildren(IncludeDescendants = true)]
        public static void SetTargetPanel(Control element, string value)
        {
            element.SetValue(TargetPanelProperty, value);
        }

        [AttachedPropertyBrowsableForChildren(IncludeDescendants = true)]
        public static string GetTargetPanel(Control element)
        {
            return (string)element.GetValue(TargetPanelProperty);
        }

        public static readonly DependencyProperty RoutedEventProperty =
            DependencyProperty.RegisterAttached("RoutedEvent", typeof(RoutedEvent), typeof(NavigationGrid), new PropertyMetadata(null, RoutedEventChanged));

        [AttachedPropertyBrowsableForChildren(IncludeDescendants = true)]
        public static void SetRoutedEvent(Control element, RoutedEvent value)
        {
            element.SetValue(RoutedEventProperty, value);
        }

        [AttachedPropertyBrowsableForChildren(IncludeDescendants = true)]
        public static RoutedEvent GetRoutedEvent(Control element)
        {
            return (RoutedEvent)element.GetValue(RoutedEventProperty);
        }

        private static void RoutedEventChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var control = obj as Control;

            var newroutedEvent = (RoutedEvent)args.NewValue;
            var oldroutedEvent = (RoutedEvent)args.OldValue;


            try
            {
                control.AddHandler(newroutedEvent, new RoutedEventHandler(GetParent(control).SwitchScreen));
            }
            catch { }

            try
            {
                control.RemoveHandler(oldroutedEvent, new RoutedEventHandler(GetParent(control).SwitchScreen));
            }
            catch { }
        }

        private static readonly DependencyProperty PositionProperty =
            DependencyProperty.RegisterAttached("Position", typeof(Rect), typeof(NavigationGrid), new PropertyMetadata(new Rect(-100, -100, 0, 0), PositionChanged));

        private static void PositionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            var parent = obj as FrameworkElement;
            var Grid = GetParent(parent);
            try
            {
                Grid.InvalidateArrange();
            }
            catch { }
        }



        public static readonly DependencyProperty TransitionProperty =
            DependencyProperty.RegisterAttached("Transition", typeof(NavigationGridAnimation), typeof(NavigationGrid), new PropertyMetadata(default(NavigationGridAnimation)));

        [AttachedPropertyBrowsableForChildren(IncludeDescendants = true)]
        public static void SetTransition(Control element, NavigationGridAnimation value)
        {
            element.SetValue(TransitionProperty, value);
        }

        [AttachedPropertyBrowsableForChildren(IncludeDescendants = true)]
        public static NavigationGridAnimation GetTransition(Control element)
        {
            return (NavigationGridAnimation)element.GetValue(TransitionProperty);
        }
        #endregion
        #endregion

        #region Events

        public static readonly RoutedEvent ScreenChangedEvent = EventManager.RegisterRoutedEvent("ScreenChanged", RoutingStrategy.Bubble,
                                                                                     typeof (RoutedEventHandler),
                                                                                     typeof (NavigationGrid));
        public event RoutedEventHandler ScreenChanged
        {
            add { AddHandler(ScreenChangedEvent, value); }
            remove { RemoveHandler(ScreenChangedEvent, value); }
        }
        #endregion

        private bool noStartupPanel = false;

        private static NavigationGrid GetParent(FrameworkElement element)
        {
            if (element.GetType() == typeof(NavigationGrid))
                return element as NavigationGrid;

            if (element.Parent == null)
                return null;

            try
            {
                return GetParent(element.Parent as FrameworkElement);
            }
            catch
            {
                return null;
            }
        }

        private void SwitchScreen(object sender, RoutedEventArgs args)
        {
            var obj = sender as UIElement;

            NextTransition = obj.GetValue(TransitionProperty) as NavigationGridAnimation;

            if (NextTransition == null)
                NextTransition = new NavigationGridAnimation();

            CurrentPanel = obj.GetValue(TargetPanelProperty) as string;

            RaiseEvent(new RoutedEventArgs(ScreenChangedEvent));
        }

        private UIElement FindWithMetadata(DependencyProperty property, object value)
        {
            return InternalChildren.Cast<UIElement>().FirstOrDefault(element => element.GetValue(property).Equals(value));
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (UIElement child in InternalChildren)
            {
                if (child.GetValue(PositionProperty).Equals(new Rect(-100, -100, 0, 0)))
                {
                    Point offset = new Point(1, 0);

                    if (child.Equals(FindWithMetadata(PanelNameProperty, CurrentPanel)))
                        offset = new Point(0, 0);

                    child.SetValue(PositionProperty, new Rect(offset, new Size(1, 1)));
                }

                child.Arrange(MathHelper.Rect_mul(((Rect)child.GetValue(PositionProperty)), new Rect(new Point(finalSize.Width, finalSize.Height), finalSize)));
            }

            return base.ArrangeOverride(finalSize);
        }

        public void NavigateTo(string name)
        { NavigateTo(name, NavigationGridAnimation.EDirection.Left); }

        public void NavigateTo(string name, NavigationGridAnimation.EDirection direction)
        { NavigateTo(name, direction, new Duration(new TimeSpan(0, 0, 0, 0, 200))); }

        public void NavigateTo(string name, NavigationGridAnimation.EDirection direction, Duration duration)
        { NavigateTo(name, direction, duration, null); }

        public void NavigateTo(string name, NavigationGridAnimation.EDirection direction, Duration duration, IEasingFunction easing)
        {
            if (!CurrentPanel.Equals(name))
            {
                NextTransition = new NavigationGridAnimation
                    {Direction = direction, Duration = duration, EasingFunction = easing};
                CurrentPanel = name;
            }

            RaiseEvent(new RoutedEventArgs(ScreenChangedEvent));
        }

        public NavigationGrid()
        {
            ClipToBounds = true;
            NextTransition = new NavigationGridAnimation();
        }
    }
}
